/**大数类 */
export default class BigNumber {
  private pn: number //正负性(1/-1)
  private intNumStr: string //整数形式的数字字符串
  private decPlaces: number //小数位数
  constructor(numStr: string | number) {
    if (typeof numStr == 'number') numStr = numStr.toString();
    if (!BigNumber.isNumStr(numStr)) throw '非数字字符串';
    let { pn, intNumStr, decPlaces } = this.analyzingNumStr(numStr);
    this.pn = pn;
    this.intNumStr = intNumStr;
    this.decPlaces = decPlaces;
  }
  //返回原数字字符串
  originalStr() {
    if (this.decPlaces == 0) return this.pIntNumStr().toString();
    let str = this.intNumStr;
    if (str.length <= this.decPlaces) str = str.padStart(this.decPlaces + 1, '0');
    let arr = str.split('');
    arr.splice(arr.length - this.decPlaces, 0, '.');

    while (arr[arr.length - 1] == '0') {
      arr.pop();
    }
    if (arr[arr.length - 1] == '.') arr.pop();

    if (this.pn == -1) arr.unshift('-');
    return arr.join('');
  }
  //是否是合法的数字字符串(正数负数小数)
  static isNumStr(str: string) {
    if (typeof str != 'string') return false;
    return /^-?(0|[1-9]\d*)(\.\d+)?$/.test(str);
  }
  //解析数字字符串
  private analyzingNumStr(numStr: string) {
    let obj: any = {};
    obj.pn = numStr[0] == '-' ? -1 : 1;
    numStr = numStr.replace('-', '');
    if (numStr.includes('.')) {
      obj.decPlaces = numStr.split('.')[1].length;
      numStr = numStr.replace('.', '');
      obj.intNumStr = this.clearNumStr(numStr);
    } else {
      obj.intNumStr = numStr;
      obj.decPlaces = 0;
    }
    return obj;
  }
  //去除数字字符串无意义的0
  private clearNumStr(numStr: string) {
    //前面
    let arr = numStr.split('');
    while (arr[0] == '0' && arr[1]) {
      arr.shift();
    }
    return arr.join('');
  }
  //以0补位
  private cover(n: number) {
    this.intNumStr = this.intNumStr.padEnd(this.intNumStr.length + n, '0');
    this.decPlaces += n;
  }
  //返回带正负的整数大数
  private pIntNumStr() {
    if (this.pn == -1) return BigInt(`-${this.intNumStr}`);
    return BigInt(this.intNumStr);
  }
  //加法
  add(bn: BigNumber | string | number) {
    if (!(bn instanceof BigNumber)) bn = new BigNumber(bn);
    //补位
    if (this.decPlaces > bn.decPlaces) bn.cover(this.decPlaces - bn.decPlaces);
    else if (this.decPlaces < bn.decPlaces) this.cover(bn.decPlaces - this.decPlaces);
    //整数运算
    let bres = new BigNumber((this.pIntNumStr() + bn.pIntNumStr()).toString());
    bres.decPlaces = this.decPlaces;
    return bres;
  }
  //减法
  sub(bn: BigNumber | string | number) {
    let sn = new BigNumber(bn instanceof BigNumber ? bn.originalStr() : bn);
    sn.pn = -sn.pn;
    return this.add(sn);
  }
  //乘法
  mul(bn: BigNumber | string | number) {
    if (!(bn instanceof BigNumber)) bn = new BigNumber(bn);
    //补位
    if (this.decPlaces > bn.decPlaces) bn.cover(this.decPlaces - bn.decPlaces);
    else if (this.decPlaces < bn.decPlaces) this.cover(bn.decPlaces - this.decPlaces);
    //整数运算
    let bres = new BigNumber((this.pIntNumStr() * bn.pIntNumStr()).toString());
    bres.decPlaces = this.decPlaces + bn.decPlaces;
    return bres;
  }
  //除法(infDec无限小数的保留位数)
  div(bn: BigNumber | string | number, infDec: number = 6) {
    if (infDec < 0) throw '小数位不能小于0';
    if (!(bn instanceof BigNumber)) bn = new BigNumber(bn);
    if (bn.intNumStr == '0') throw '除数不能为0';
    //补位
    if (this.decPlaces > bn.decPlaces) bn.cover(this.decPlaces - bn.decPlaces);
    else if (this.decPlaces < bn.decPlaces) this.cover(bn.decPlaces - this.decPlaces);
    //整除
    if (this.pIntNumStr() % bn.pIntNumStr() == 0n) return new BigNumber((this.pIntNumStr() / bn.pIntNumStr()).toString());
    //非整除
    let islimited = BigNumber.isLimitedDecimal(BigInt(bn.intNumStr));
    let res = '', m1 = BigInt(this.intNumStr), m2 = BigInt(bn.intNumStr);
    res += m1 / m2 + '.';
    m1 = m1 % m2;
    while (m1 != 0n) {
      m1 = m1 * 10n;
      if (m1 >= m2) {
        res += m1 / m2;
        m1 = m1 % m2;
      } else {
        res += '0';
      }
      if (!islimited && (--infDec) == 0) break;//无限小数的保留位数
    }
    if (this.pn * bn.pn == -1) res = `-${res}`
    return new BigNumber(res);
  }
  //最简分数，通过分母判断是否有限小数
  //当除数（分母）只有2或者5这两个质因数时，就是有限小数；当除数（分母）含有2和5以外的质因数时就是无限循环小数
  static isLimitedDecimal(denNum: bigint): boolean {
    if (denNum == 1n) return true;
    if (denNum % 2n == 0n) return this.isLimitedDecimal(denNum / 2n);
    else if (denNum % 5n == 0n) return this.isLimitedDecimal(denNum / 5n);
    else return false;
  }
}

